package com.zslc.foreignoption.util;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.io.file.FileCopier;
import cn.hutool.core.io.file.Tailer;
import cn.hutool.core.lang.Assert;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;


public class FileUtils {
    public static final String CLASS_EXT = ".class";
    public static final String JAR_FILE_EXT = ".jar";
    public static final String JAR_PATH_EXT = ".jar!";
    public static final String PATH_FILE_PRE = "file:";
    private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
    private static final char UNIX_SEPARATOR = '/';
    private static final char WINDOWS_SEPARATOR = '\\';
    private static final Pattern FILE_NAME_INVALID_PATTERN_WIN = Pattern.compile("[\\\\/:*?\"<>|]");

    public FileUtils() {
    }

    public static boolean isWindows() {
        return '\\' == File.separatorChar;
    }



    public static String getFileMd5(String path) throws IOException {
        return DigestUtils.md5DigestAsHex(new FileInputStream(path));
    }





    private static File[] verifiedListFiles(File directory) throws IOException {
        String message;
        if (!directory.exists()) {
            message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        } else if (!directory.isDirectory()) {
            message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        } else {
            File[] files = directory.listFiles();
            if (files == null) {
                throw new IOException("Failed to list contents of " + directory);
            } else {
                return files;
            }
        }
    }




    private static boolean isBrokenSymlink(File file) throws IOException {
        if (file.exists()) {
            return false;
        } else {
            File canon = file.getCanonicalFile();
            File parentDir = canon.getParentFile();
            if (parentDir != null && parentDir.exists()) {
                File[] fileInDir = parentDir.listFiles((aFile) -> {
                    return aFile.equals(canon);
                });
                return fileInDir != null && fileInDir.length > 0;
            } else {
                return false;
            }
        }
    }

    public static boolean isEmpty(File file) {
        if (null == file) {
            return true;
        } else if (file.isDirectory()) {
            String[] subFiles = file.list();
            return ArrayUtils.isEmpty(subFiles);
        } else if (file.isFile()) {
            return file.length() <= 0L;
        } else {
            return false;
        }
    }

    public static boolean isNotEmpty(File file) {
        return !isEmpty(file);
    }

    public static boolean isDirEmpty(Path dirPath) {
        try {
            DirectoryStream<Path> dirStream = Files.newDirectoryStream(dirPath);
            Throwable var2 = null;

            boolean var3;
            try {
                var3 = !dirStream.iterator().hasNext();
            } catch (Throwable var13) {
                var2 = var13;
                throw var13;
            } finally {
                if (dirStream != null) {
                    if (var2 != null) {
                        try {
                            dirStream.close();
                        } catch (Throwable var12) {
                            var2.addSuppressed(var12);
                        }
                    } else {
                        dirStream.close();
                    }
                }

            }

            return var3;
        } catch (IOException var15) {
            throw new IORuntimeException(var15);
        }
    }

    public static boolean isDirEmpty(File dir) {
        return isDirEmpty(dir.toPath());
    }

    public static List<File> loopFiles(String path, FileFilter fileFilter) {
        return loopFiles(file(path), fileFilter);
    }

    public static List<File> loopFiles(File file, FileFilter fileFilter) {
        List<File> fileList = new ArrayList();
        if (null != file && file.exists()) {
            if (file.isDirectory()) {
                File[] subFiles = file.listFiles();
                if (ArrayUtils.isNotEmpty(subFiles)) {
                    File[] var4 = subFiles;
                    int var5 = subFiles.length;

                    for (int var6 = 0; var6 < var5; ++var6) {
                        File tmp = var4[var6];
                        fileList.addAll(loopFiles(tmp, fileFilter));
                    }
                }
            } else if (null == fileFilter || fileFilter.accept(file)) {
                fileList.add(file);
            }

            return fileList;
        } else {
            return fileList;
        }
    }



    public static void walkFiles(Path start, int maxDepth, FileVisitor<? super Path> visitor) {
        if (maxDepth < 0) {
            maxDepth = Integer.MAX_VALUE;
        }

        try {
            Files.walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), maxDepth, visitor);
        } catch (IOException var4) {
            throw new IORuntimeException(var4);
        }
    }

    public static List<File> loopFiles(String path) {
        return loopFiles(file(path));
    }

    public static List<File> loopFiles(File file) {
        return loopFiles(file, null);
    }



    public static File newFile(String path) {
        return new File(path);
    }

    public static File file(String parent, String path) {
        return file(new File(parent), path);
    }


    public static File file(File directory, String... names) {
        Assert.notNull(directory, "directorydirectory must not be null");
        if (ArrayUtils.isEmpty(names)) {
            return directory;
        } else {
            File file = directory;
            String[] var3 = names;
            int var4 = names.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                String name = var3[var5];
                if (null != name) {
                    file = file(file, name);
                }
            }

            return file;
        }
    }

    public static File file(String... names) {
        if (ArrayUtils.isEmpty(names)) {
            return null;
        } else {
            File file = null;
            String[] var2 = names;
            int var3 = names.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                String name = var2[var4];
                if (file == null) {
                    file = file(name);
                } else {
                    file = file(file, name);
                }
            }

            return file;
        }
    }

    public static File file(URI uri) {
        if (uri == null) {
            throw new NullPointerException("File uri is null!");
        } else {
            return new File(uri);
        }
    }

    public static String getTmpDirPath() {
        return System.getProperty("java.io.tmpdir");
    }

    public static File getTmpDir() {
        return file(getTmpDirPath());
    }

    public static String getUserHomePath() {
        return System.getProperty("user.home");
    }

    public static File getUserHomeDir() {
        return file(getUserHomePath());
    }

    public static boolean exist(String path) {
        return null != path && file(path).exists();
    }

    public static boolean exist(File file) {
        return null != file && file.exists();
    }

    public static boolean exist(String directory, String regexp) {
        File file = new File(directory);
        if (!file.exists()) {
            return false;
        } else {
            String[] fileList = file.list();
            if (fileList == null) {
                return false;
            } else {
                String[] var4 = fileList;
                int var5 = fileList.length;

                for (int var6 = 0; var6 < var5; ++var6) {
                    String fileName = var4[var6];
                    if (fileName.matches(regexp)) {
                        return true;
                    }
                }

                return false;
            }
        }
    }

    public static Date lastModifiedTime(File file) {
        return !exist(file) ? null : new Date(file.lastModified());
    }

    public static Date lastModifiedTime(String path) {
        return lastModifiedTime(new File(path));
    }



    public static boolean newerThan(File file, File reference) {
        return null == reference || !reference.exists() || newerThan(file, reference.lastModified());
    }

    public static boolean newerThan(File file, long timeMillis) {
        if (null != file && file.exists()) {
            return file.lastModified() > timeMillis;
        } else {
            return false;
        }
    }

    public static File touch(String fullFilePath) throws IORuntimeException {
        return fullFilePath == null ? null : touch(file(fullFilePath));
    }

    public static File touch(File file) throws IORuntimeException {
        if (null == file) {
            return null;
        } else {
            if (!file.exists()) {
                mkParentDirs(file);

                try {
                    file.createNewFile();
                } catch (Exception var2) {
                    throw new IORuntimeException(var2);
                }
            }

            return file;
        }
    }

    public static File touch(File parent, String path) throws IORuntimeException {
        return touch(file(parent, path));
    }

    public static File touch(String parent, String path) throws IORuntimeException {
        return touch(file(parent, path));
    }

    public static File mkParentDirs(File file) {
        File parentFile = file.getParentFile();
        if (null != parentFile && !parentFile.exists()) {
            parentFile.mkdirs();
        }

        return parentFile;
    }

    public static File mkParentDirs(String path) {
        return path == null ? null : mkParentDirs(file(path));
    }

    public static boolean del(String fullFileOrDirPath) throws IORuntimeException {
        return del(file(fullFileOrDirPath));
    }

    public static boolean del(File file) throws IORuntimeException {
        if (file != null && file.exists()) {
            if (file.isDirectory()) {
                boolean isOk = clean(file);
                if (!isOk) {
                    return false;
                }
            }

            return file.delete();
        } else {
            return true;
        }
    }

    public static boolean clean(String dirPath) throws IORuntimeException {
        return clean(file(dirPath));
    }

    public static boolean clean(File directory) throws IORuntimeException {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (null != files) {
                File[] var3 = files;
                int var4 = files.length;

                for (int var5 = 0; var5 < var4; ++var5) {
                    File childFile = var3[var5];
                    boolean isOk = del(childFile);
                    if (!isOk) {
                        return false;
                    }
                }
            }

            return true;
        } else {
            return true;
        }
    }

    public static boolean cleanEmpty(File directory) throws IORuntimeException {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (ArrayUtils.isEmpty(files)) {
                directory.delete();
            } else {
                File[] var2 = files;
                int var3 = files.length;

                for (int var4 = 0; var4 < var3; ++var4) {
                    File childFile = var2[var4];
                    cleanEmpty(childFile);
                }
            }

            return true;
        } else {
            return true;
        }
    }

    public static File mkdir(String dirPath) {
        if (dirPath == null) {
            return null;
        } else {
            File dir = file(dirPath);
            return mkdir(dir);
        }
    }

    public static File mkdir(File dir) {
        if (dir == null) {
            return null;
        } else {
            if (!dir.exists()) {
                dir.mkdirs();
            }

            return dir;
        }
    }

    public static File createTempFile(File dir) throws IORuntimeException {
        return createTempFile("hutool", null, dir, true);
    }

    public static File createTempFile(File dir, boolean isReCreat) throws IORuntimeException {
        return createTempFile("hutool", null, dir, isReCreat);
    }

    public static File createTempFile(String prefix, String suffix, File dir, boolean isReCreat) throws IORuntimeException {
        int exceptionsCount = 0;

        while (true) {
            try {
                File file = File.createTempFile(prefix, suffix, dir).getCanonicalFile();
                if (isReCreat) {
                    file.delete();
                    file.createNewFile();
                }

                return file;
            } catch (IOException var6) {
                ++exceptionsCount;
                if (exceptionsCount >= 50) {
                    throw new IORuntimeException(var6);
                }
            }
        }
    }

    public static File copyFile(String src, String dest, StandardCopyOption... options) throws IORuntimeException {
        Assert.notBlank(src, "Source File path is blank !");
        Assert.notNull(src, "Destination File path is null !");
        return copyFile(Paths.get(src), Paths.get(dest), options).toFile();
    }

    public static File copyFile(File src, File dest, StandardCopyOption... options) throws IORuntimeException {
        Assert.notNull(src, "Source File is null !");
        if (!src.exists()) {
            throw new IORuntimeException("File not exist: " + src);
        } else {
            Assert.notNull(dest, "Destination File or directiory is null !");
            if (equals(src, dest)) {
                throw new IORuntimeException("Files '{}' and '{}' are equal", src, dest);
            } else {
                return copyFile(src.toPath(), dest.toPath(), options).toFile();
            }
        }
    }

    public static Path copyFile(Path src, Path dest, StandardCopyOption... options) throws IORuntimeException {
        Assert.notNull(src, "Source File is null !");
        Assert.notNull(dest, "Destination File or directiory is null !");
        Path destPath = dest.toFile().isDirectory() ? dest.resolve(src.getFileName()) : dest;

        try {
            return Files.copy(src, destPath, options);
        } catch (IOException var5) {
            throw new IORuntimeException(var5);
        }
    }

    public static File copy(String srcPath, String destPath, boolean isOverride) throws IORuntimeException {
        return copy(file(srcPath), file(destPath), isOverride);
    }

    public static File copy(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setOverride(isOverride).copy();
    }

    public static File copyContent(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setCopyContentIfDir(true).setOverride(isOverride).copy();
    }

    public static File copyFilesFromDir(File src, File dest, boolean isOverride) throws IORuntimeException {
        return FileCopier.create(src, dest).setCopyContentIfDir(true).setOnlyCopyFile(true).setOverride(isOverride).copy();
    }



    public static File rename(File file, String newName, boolean isRetainExt, boolean isOverride) {
        if (isRetainExt) {
            newName = newName.concat(".").concat(extName(file));
        }

        Path path = file.toPath();
        CopyOption[] options = isOverride ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[0];

        try {
            return Files.move(path, path.resolveSibling(newName), options).toFile();
        } catch (IOException var7) {
            throw new IORuntimeException(var7);
        }
    }

    public static String getCanonicalPath(File file) {
        if (null == file) {
            return null;
        } else {
            try {
                return file.getCanonicalPath();
            } catch (IOException var2) {
                throw new IORuntimeException(var2);
            }
        }
    }



    public static String getAbsolutePath(File file) {
        if (file == null) {
            return null;
        } else {
            try {
                return file.getCanonicalPath();
            } catch (IOException var2) {
                return file.getAbsolutePath();
            }
        }
    }

    public static boolean isAbsolutePath(String path) {
        if (StringUtils.isEmpty(path)) {
            return false;
        } else {
            return '/' == path.charAt(0) || path.matches("^[a-zA-Z]:([/\\\\].*)?");
        }
    }

    public static boolean isDirectory(String path) {
        return null != path && file(path).isDirectory();
    }

    public static boolean isDirectory(File file) {
        return null != file && file.isDirectory();
    }

    public static boolean isDirectory(Path path, boolean isFollowLinks) {
        if (null == path) {
            return false;
        } else {
            LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
            return Files.isDirectory(path, options);
        }
    }

    public static boolean isFile(String path) {
        return null != path && file(path).isFile();
    }

    public static boolean isFile(File file) {
        return null != file && file.isFile();
    }

    public static boolean isFile(Path path, boolean isFollowLinks) {
        if (null == path) {
            return false;
        } else {
            LinkOption[] options = isFollowLinks ? new LinkOption[0] : new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
            return Files.isRegularFile(path, options);
        }
    }

    public static boolean equals(File file1, File file2) throws IORuntimeException {
        Assert.notNull(file1);
        Assert.notNull(file2);
        if (file1.exists() && file2.exists()) {
            try {
                return Files.isSameFile(file1.toPath(), file2.toPath());
            } catch (IOException var3) {
                throw new IORuntimeException(var3);
            }
        } else {
            return !file1.exists() && !file2.exists() && pathEquals(file1, file2);
        }
    }




    public static boolean pathEquals(File file1, File file2) {
        if (isWindows()) {
            try {
                if (StringUtils.equalsIgnoreCase(file1.getCanonicalPath(), file2.getCanonicalPath())) {
                    return true;
                }
            } catch (Exception var3) {
                if (StringUtils.equalsIgnoreCase(file1.getAbsolutePath(), file2.getAbsolutePath())) {
                    return true;
                }
            }
        } else {
            try {
                if (StringUtils.equals(file1.getCanonicalPath(), file2.getCanonicalPath())) {
                    return true;
                }
            } catch (Exception var4) {
                if (StringUtils.equals(file1.getAbsolutePath(), file2.getAbsolutePath())) {
                    return true;
                }
            }
        }

        return false;
    }



    public static boolean isModifed(File file, long lastModifyTime) {
        if (null != file && file.exists()) {
            return file.lastModified() != lastModifyTime;
        } else {
            return true;
        }
    }





    public static Path getPathEle(Path path, int index) {
        return subPath(path, index, index == -1 ? path.getNameCount() : index + 1);
    }

    public static Path getLastPathEle(Path path) {
        return getPathEle(path, path.getNameCount() - 1);
    }

    public static Path subPath(Path path, int fromIndex, int toIndex) {
        if (null == path) {
            return null;
        } else {
            int len = path.getNameCount();
            if (fromIndex < 0) {
                fromIndex += len;
                if (fromIndex < 0) {
                    fromIndex = 0;
                }
            } else if (fromIndex > len) {
                fromIndex = len;
            }

            if (toIndex < 0) {
                toIndex += len;
                if (toIndex < 0) {
                    toIndex = len;
                }
            } else if (toIndex > len) {
                toIndex = len;
            }

            if (toIndex < fromIndex) {
                int tmp = fromIndex;
                fromIndex = toIndex;
                toIndex = tmp;
            }

            return fromIndex == toIndex ? null : path.subpath(fromIndex, toIndex);
        }
    }

    public static String getName(File file) {
        return null != file ? file.getName() : null;
    }


    public static String extName(File file) {
        if (null == file) {
            return null;
        } else {
            return file.isDirectory() ? null : extName(file.getName());
        }
    }

    public static String extName(String fileName) {
        if (fileName == null) {
            return null;
        } else {
            int index = fileName.lastIndexOf(".");
            if (index == -1) {
                return "";
            } else {
                String ext = fileName.substring(index + 1);
                return StringUtils.containsAny(ext, '/', '\\') ? "" : ext;
            }
        }
    }


    public static void tail(File file, Charset charset, LineHandler handler) {
        (new Tailer(file, charset, handler)).start();
    }

    public static void tail(File file, Charset charset) {
        tail(file, charset, Tailer.CONSOLE_HANDLER);
    }
}
